Repository navigation
fix(platform): repair failures found in Cloudflare acceptance - #569
Merged
Merged
Conversation
🚀 Deploying Preview to Cloudflare 🚀Preview URL: https://codex-platform-e2e-20261023-void-cloud.voidzero-docs.workers.dev (commit 21bb87e)This URL reflects your latest Preview deploymentPreview Deployments by commit
|
Brooooooklyn
marked this pull request as ready for review
October 23, 2026 11:29
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 723240e248
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
Brooooooklyn
added a commit
to davidgustys/void
that referenced
this pull request
Oct 26, 2026
…an old secret version Try the automatic app rollback without force first, as Wrangler's rollback does. Only when Cloudflare answers 10220 (a secret changed since the previous version was active), read the owner again and retry with force, so a secret changed outside Void is not overwritten and any other Cloudflare check still applies. Keep the trigger-sync rollback and the platform installer unforced. The first returns to the version that served traffic seconds before, so a 10220 there comes from someone else. The second would undo voidzero-dev#569: a credential update rolls forward instead of restoring old secrets. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Brooooooklyn
added a commit
to davidgustys/void
that referenced
this pull request
Oct 26, 2026
…an old secret version Try the automatic app rollback without force first, as Wrangler's rollback does. Only when Cloudflare answers 10220 (a secret changed since the previous version was active), read the owner again and retry with force, so a secret changed outside Void is not overwritten and any other Cloudflare check still applies. Keep the trigger-sync rollback and the platform installer unforced. The first returns to the version that served traffic seconds before, so a 10220 there comes from someone else. The second would undo voidzero-dev#569: a credential update rolls forward instead of restoring old secrets. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Brooooooklyn
added a commit
that referenced
this pull request
Oct 26, 2026
Cloudflare refuses a deployment that moves traffic back to an older
Worker version when a secret has changed since that version was active
(error 10220), unless the request is forced. Void's automatic recovery
runs into this. After a failed readiness check, activation or trigger
sync, it deploys the previous version at 100%. Cloudflare rejects that,
the Worker stays at the previous version at 100% plus the failed one at
0%, and the next deploy stops with `deployment-changed` until someone
fixes the deployment by hand.
```
before
deploy v2 (changes a secret) → readiness fails → restore v1@100
→ Cloudflare 10220 ✗ → stuck at v1@100 + v2@0, next deploy refuses
after
deploy v2 (changes a secret) → readiness fails → restore v1@100
→ Cloudflare 10220 → Void still owns traffic? ── no ──→ stop ('unsafe')
│ yes
└→ restore v1@100 with force ✓
→ error says: old secret values are live,
deploy again to apply the new ones
```
## What changed
- `versions.deploy` gets an optional `bypassDeploymentChecks`. Both cf
runners pass it as `--bypass-deployment-checks`, which sends
`force=true` to the Deployments API.
- `restorePreviousVersion` (readiness, uploader-check, activation and
trigger-sync failures) first tries the restore without force. Only when
Cloudflare answers 10220 does it read the owner again and retry with
force. Wrangler's own rollback does the same: it tries without force and
forces only after a 10220.
- When the restore had to be forced, the activation result carries
`restoredOlderSecrets: true` and the deploy error adds:
> Cloudflare refused to restore the previous version because this deploy
changed a secret, so Void forced the restore. The previous version runs
with its old secret values; deploy again to apply the new ones.
| Deploy | Forced? |
|---|---|
| normal deploy, staging, activation | never |
| `void rollback` (user asked for it) | never |
| automatic app rollback (`restorePreviousVersion`) | only on retry
after 10220, if Void still owns traffic |
| trigger-sync rollback in `activateCloudflareExistingVersion` | never |
| platform installer activation and restoration | never |
### Why the last two are not forced
- **Trigger-sync rollback:** it goes back to the version that served
traffic seconds before, and Cloudflare had just accepted the user's
rollback without force. So a 10220 there means someone else changed a
secret in between, and Void must not overwrite that.
- **Platform installer:** #569 decided that a credential update is never
rolled back to versions holding the old secrets. Traffic stays closed
and the next run rolls forward (see the comment in
`platform-installer.ts` and the test "rolls forward a partially restored
credential update"). Forcing `restorePlatformWorkerDeployment` would
undo that for the one Worker whose trigger or domain publish failed, and
put it back on the old, possibly revoked, API token. Without a
credential update, Void's own platform deploys don't cause a 10220.
## Where this applies
The app path is the one that hits this: a deploy whose new version
changes a secret and then fails readiness, activation or trigger sync. I
found it in a fork that publishes prebuilt artifacts with explicit
secrets. With this change, the same live run on Cloudflare restored the
previous version and the next deploy went through.
## Tests
- `operations.test.ts` runs every recovery path (readiness, activation,
triggers; with and without `preserveTriggers`) in 4 cases:
- Cloudflare accepts the restore: nothing is forced.
- Cloudflare refuses with 10220: one unforced try, an owner read, then
one forced retry, and `restoredOlderSecrets: true`.
- 10220, and someone else deployed in between: result `unsafe`, no
forced retry.
- Any other error: result `failed`, no retry.
- `cloudflare-deploy.test.ts` checks that the readiness, trigger and
uploader errors add the old-secrets line only when
`restoredOlderSecrets` is set.
- `cloudflare-operations.test.ts` checks that the trigger-sync rollback
and user rollbacks are not forced. `platform-install-cutover.test.ts`
checks that no platform activation or restoration is forced.
- One test runs the real pinned cf (1.0.0-beta.18) against a local
server and checks that `?force=true` is sent next to the deployment
body.
- Mutation checks, each caught by at least one test: always force, never
retry, retry on any error, skip the owner re-read, force the
trigger-sync rollback, force the platform restoration, force the
platform activation, drop or always set `restoredOlderSecrets`, drop it
on the trigger path, skip the message line.
- `vp fmt`, `vp lint`, the `deploy-cloudflare` and `void` typechecks,
and `vp run test --base origin/main` pass.
## Known limits
- Cloudflare describes `force` only as skipping checks "such as" the
secret one. This PR forces only after Cloudflare answers 10220, so force
is used only in the case Cloudflare names.
- Between Void's owner read and the forced retry there is still a short
gap, about one cf process start. The Deployments API has no
compare-and-set, so it can't be closed fully. That gap already exists
for every deploy Void makes.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
---------
Co-authored-by: LongYinan <github@lyn.one>
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Live Cloudflare acceptance exposed failures in interrupted platform updates, slow application requests, usage reporting, page revalidation and account prerequisite guidance. This change fixes those paths and preserves the provider resources used by repeatable live tests.
Changes
Validation
Final local verification on
21bb87ef0:vp test runpassed 736 files and 14,547 tests (31 tests skipped, zero unhandled errors), and all 33 package typechecks passed. The prerequisite-message tests pass 184 focused cases; the legacy-KV follow-up passes 39 focused cases. Both path-specific and full-purge endpoint regressions failed before that fix and pass afterward. Affected verification passed 10,974 tests for the CLI changes and 4,907 for the cache follow-up. Core runtime/package and documentation builds, formatting and lint passed. Independent reviews found no remaining actionable issues in these changes.A rebuilt CLI was tested against the unpaid account: the real installation plan reports R2 code 10042 and Workers for Platforms code 10121 together, with account-specific links and retry instructions. It exits before creating installation files, and the existing platform registry is unchanged. The captured real Analytics Engine code 10089 failure was replayed through the updated formatter and now directs the operator to Enable and the appropriate recovery command.
The free-account comparison also verified a native static app and stock Void AI Gateway creation. Analytics Engine binding upload failed with 10089 before activation and passed after zero-price dashboard activation while Workers Free remained selected. R2 was not activated and WfP was not purchased; a fresh managed platform cannot complete installation on that account until those prerequisites are satisfied. Airwallex’s generic AI Gateway 500/code 7000 was not reproduced.
Both paid-account Cloudflare installations were deployed and independently verified on
852341fc6with packaged manifest34f1cea97be8251055b5002ec9eb2cdaa83393be73b81cd6f23532daafb00a25. Provider readback verified all five active Worker versions at 100% traffic, all 19/15 retained resource IDs, settings and migration history. Three 600-second page-action purges and one all-project purge returned fresh first-load MISS content in the same Cloudflare location, with independent R2 before/deleted/replacement readback. Project usage returns HTTP 200; AI usage matches the provider aggregate after whole-neuron rounding. All 24 managed apps passed final reachability; the kitchen smoke passed 14/14.That live acceptance covered an upgraded and a fresh installation, 24 managed apps (239 functional checks; 81 browser checks across 16 apps), four native app classes, two PostgreSQL variants, two genuine users, teams and ownership, admin/user settings, quota/suspension controls, protected apps, connected-app publishing/private preview, inbound/outbound email, queues, cron, AI, Live SSE, WebSockets and Sandbox. Earlier local acceptance also covered 243 real PostgreSQL checks, playground builds and packed npm/pnpm consumers for four adapters.
Remote CI, Platform CI, Prepublish Test, and Security Analysis completed successfully on
21bb87ef0: 19 successful checks, one skipped, none failing or pending. The skipped credentialedlive-e2ejob is separate from the provider-backed acceptance above. Automated review completed on this head with no findings; the legacy-KV review thread is resolved.Known limitations
The legacy-KV cleanup follow-up has local endpoint regression coverage and has not been redeployed to the acceptance installations; their runtime remains
852341fc6. No existing MySQL service was available, so MySQL has local wire-protocol coverage only. Verified-email invitation acceptance, direct Google/GitHub account linking, optional GitHub build services and third-party AI BYOK were not configured. Successful project custom-domain attachment remains unverified: the test installation disables that capability, and its denial path preserves DNS and the existing app. Destructive remote teardown is excluded by the retained-resource test policy. An ambiguous pre-fix email receipt remains preserved; recovery and a duplicate send have not been attempted. No additional paid plan was purchased.